package com.ruoyi.common.exception.global;

import com.ruoyi.common.bean.ResultData;
import com.ruoyi.common.exception.component.ExceptionAlterUtil;
import com.ruoyi.common.log.TaskTrace;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ObjectUtils;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;

/**
 * 异常处理器
 *
 * @author hanyi
 */
@RestControllerAdvice
@Slf4j
public class CoreExceptionHandler {

    @Resource
    private ExceptionAlterUtil exceptionAlterUtil;

    @ExceptionHandler(value = {MethodArgumentNotValidException.class})
    @TaskTrace
    public ResultData<?> handleValidException(MethodArgumentNotValidException e) {
        String defaultMessage = Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage();
        return ResultData.error(BusinessErrorEnums.INVALID_PARAMETER.getErrorCode(), defaultMessage);
    }

    /**
     * CustomException 视为正常的业务异常
     */
    @ExceptionHandler({CustomException.class})
    @TaskTrace
    public ResultData<?> businessExceptionHandle(CustomException e) {
        log.error("CustomException exception:{}", e.getMessage());
        Integer code = e.getCode();
        if (ObjectUtils.isEmpty(code)) {
            code = BusinessErrorEnums.SYSTEM_INTERNAL_ERROR.getErrorCode();
        }
        return ResultData.error(e.getMessage(), code);
    }

    /**
     * 处理请求方法不正确的异常
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @TaskTrace
    public ResultData<?> handleMethodNotSupportedException(HttpRequestMethodNotSupportedException e, HttpServletRequest request) {
        String supportedMethods = String.join(", ", e.getSupportedMethods());

        exceptionAlterUtil.sendExceptionAlert(e, "请求方法不正确", "请求路径: " + request.getRequestURI() + ", 支持的方法: " + supportedMethods + ", 实际调用方法: " + request.getMethod());

        return ResultData.error(BusinessErrorEnums.INVALID_HTTP_METHOD.getErrorCode(), String.format("请求方法不正确，支持的方法为 [%s]，但实际调用了 [%s]", supportedMethods, request.getMethod()));
    }

    /**
     * 处理缺失请求参数的异常
     * 当控制器方法中要求的请求参数未被提供时，Spring会抛出MissingServletRequestParameterException
     * 此方法拦截该异常，并返回友好的错误响应
     *
     * @param e       缺失请求参数异常对象，从中可以获取缺失参数的名称
     * @param request HTTP请求对象，用于获取请求路径信息
     * @return 返回包含错误信息的ResultData对象
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    @TaskTrace
    public ResultData<?> handleMissingParameterException(MissingServletRequestParameterException e, HttpServletRequest request) {
        String paramName = e.getParameterName();
        exceptionAlterUtil.sendExceptionAlert(e, "缺失请求参数异常", "接口:" + request.getRequestURI() + ", 缺少必要参数:" + paramName);
        return ResultData.error(BusinessErrorEnums.INVALID_PARAMETER.getErrorCode(), "缺少必要参数");
    }

    @ExceptionHandler({Exception.class})
    @TaskTrace
    public ResultData<?> exceptionHandle(Exception e, HttpServletRequest request) {
        try {
            log.error("全局异常拦截 - 请求路径: {}, 异常信息:", request.getRequestURI(), e);
            String queryParam = exceptionAlterUtil.getQueryParam();
            String authorization = exceptionAlterUtil.getAuthorizationHeader();
            exceptionAlterUtil.sendExceptionAlert(e, "全局异常拦截", "请求路径:" + request.getRequestURI() + "\n" + "请求头authorization:" + authorization + "\n" + "请求参数:" + queryParam);

        } catch (Exception ex) {
            log.warn("request 为空");
            exceptionAlterUtil.sendExceptionAlert(e, "全局异常拦截");
        }
        return ResultData.error(BusinessErrorEnums.SYSTEM_INTERNAL_ERROR);
    }
}
